// $Id: PrimaryKey.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Generic primary keys.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by a primary key.</summary>
    /// <seealso cref="PrimaryKey{T}" />
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IPrimaryKey
    {

        /// <summary>Gets the internal value of the primary key.</summary>
        /// <value>The internal value of the primary key.</value>
        object Value
        {
            get;
        }

    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Contains useful methods related to the management of primary keys.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    internal static class PrimaryKey
    {

        /// <summary>Creates a temporary primary key.</summary>
        /// <remarks>Temporary primary keys are useful when you have to create new instances of a
        /// <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" />, that may be required to have
        /// a primary key though they have not been persisted yet.</remarks>
        /// <typeparam name="TPrimaryKey">The type of the instance to create.</typeparam>
        /// <returns>The instance of the new temporary primary key.</returns>
        public static TPrimaryKey CreateTemporary<TPrimaryKey>()
            where TPrimaryKey:
                IPrimaryKey,
                new()
        {
            return new TPrimaryKey();
        }
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Generic primary key.</summary>
    /// <remarks>A <see cref="PrimaryKey{T}" /> is immutable. It is called immutable (read-only) because its value cannot be modified once it has been created.</remarks>
    /// <example>
    /// <code>
    /// public class Int32PrimaryKey:
    ///     PrimaryKey&lt;int&gt;
    /// {
    ///
    ///     public PrimaryKey():
    ///         base()
    ///     {
    ///     }
    ///
    ///     public PrimaryKey(int key):
    ///         base(key)
    ///     {
    ///     }
    ///
    ///     public static implicit operator Int32PrimaryKey(int key)
    ///     {
    ///         return new Int32PrimaryKey(key);
    ///     }
    ///
    ///     public static explicit operator int(Int32PrimaryKey key)
    ///     {
    ///         return key.Value;
    ///     }
    ///
    /// }
    /// </code>
    /// </example>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public class PrimaryKey<T>:
        IPrimaryKey
    {

        /// <summary>Initializes a new instance of the <see cref="PrimaryKey{T}" /> class.</summary>
        /// <remarks>This constructor can be used to create a temporary primary key.</remarks>
        protected PrimaryKey()
        {
        }

        /// <summary>Constructor.</summary>
        /// <param name="key">Internal value of the primary key.</param>
        protected PrimaryKey(T key)
        {
            _Key=key;
            _IsInitialized=true;
        }

        /// <summary>Serves as a hash function for the primary key. <see cref="GetHashCode" /> is suitable for use in hashing algorithms and data structures like a hash table.</summary>
        public override int GetHashCode()
        {
            if (_IsInitialized)
                return Value.GetHashCode();

            return base.GetHashCode();
        }

        /// <summary>Determines whether the specified <see cref="Object" /> is equal to the current primary key.</summary>
        /// <remarks>
        /// <para>A temporary key may only be equal to itself.</para>
        /// <para>Two primary keys are equal if they are not temporary and they hold the same <see cref="Value" />.</para>
        /// </remarks>
        public override bool Equals(object obj)
        {
            return (
                base.Equals(obj) || (
                    (obj!=null) &&
                    (GetType()==obj.GetType()) &&
                    _IsInitialized &&
                    ((PrimaryKey<T>)obj)._IsInitialized &&
                    (Value.Equals(((PrimaryKey<T>)obj).Value))
                )
            );
        }

        /// <summary>Returns a <see cref="String" /> that represents the current key.</summary>
        public override string ToString()
        {
            return Value.ToString();
        }

        /// <summary>Gets the internal value of the primary key.</summary>
        /// <value>The internal value of the primary key.</value>
        public T Value
        {
            get
            {
                if (!_IsInitialized)
                    throw new InvalidOperationException();

                return _Key;
            }
        }

        /// <summary>Gets the internal value of the primary key.</summary>
        /// <value>The internal value of the primary key.</value>
        object IPrimaryKey.Value
        {
            get
            {
                return Value;
            }
        }

        /// <summary>Internal value of the primary key.</summary>
        private T _Key;
        private bool _IsInitialized;

    }

}
